package com.diy.sigmund.diyorder.feign.fallback;

import cn.hutool.aop.ProxyUtil;
import cn.hutool.core.util.StrUtil;
import com.diy.sigmund.diycommon.entity.ResultMessage;
import feign.FeignException;
import feign.Request;
import feign.RetryableException;
import feign.Target;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;

/**
 * @author ylm-sigmund
 * @see <a href="https://blog.csdn.net/jjxxww12/article/details/112902324">SpringCloud+Feign+Hystrix统一FallbackFactory降级处理</a>
 * @since 2022/6/11 10:51
 */
@Component
public class FeignFallbackFactory<T> extends CustomFallbackFactory<T> {

    private static final Logger log = LoggerFactory.getLogger(FeignFallbackFactory.class);
    private static final ThrowableAnalyzer THROWABLE_ANALYZER = new DefaultThrowableAnalyzer();

    @Override
    public T create(Method method, Throwable cause) {
        return createFallbackService(cause, method.getDeclaringClass());
    }

    private T createFallbackService(Throwable ex, Class<?> targetClass) {
        Throwable[] causeChain = THROWABLE_ANALYZER.determineCauseChain(ex);
        RetryableException ase1 = (RetryableException) THROWABLE_ANALYZER.getFirstThrowableOfType(
                RetryableException.class, causeChain);
        log.error("服务出错了，进入FallbackFactory熔断降级", ex);
        if (ase1 != null) {
            return toResponse("服务[{}]接口调用超时！", ase1.request());
        }
        FeignException ase2 = (FeignException) THROWABLE_ANALYZER.getFirstThrowableOfType(FeignException.class,
                causeChain);
        if (ase2 != null) {
            return toResponse("服务[{}]接口调用出错了！", ase2.request());
        }

        // 创建一个JDK代理类
        return ProxyUtil.newProxyInstance((proxy, method, args) -> ResultMessage.failed(ex.getMessage()), targetClass);
    }

    private T toResponse(String respMsg, Request request) {
        Target<?> target = request.requestTemplate().feignTarget();
        Class<?> targetClazz = target.type();
        String serviceName = target.name();
        return ProxyUtil.newProxyInstance((proxy, method, args) -> ResultMessage.failed(
                StrUtil.format(respMsg, serviceName)), targetClazz);
    }
}

